// "use strict";
// const createjs = require("easeljs");require("tweenjs");
import createjs from 'createjs-ts'
// import { snakes as traditions } from './tradition.js';
const Point = createjs.Point;
const Matrix2D = createjs.Matrix2D;

import {
	PLAYGROUND_WIDTH,
	PLAYGROUND_HEIGHT,
	GROW_SCORE_POINT,
	INIT_SPEED,
	ROTATE_SPEED,
	INTERVAL_PATH_POINT,
	NORMAL_AI_UPDATE_INTERVAL,
	PLAYER_INIT_LENGTH
} from './Setting.js';
import UniqueID from './UniqueID.js';
import MapManager from './MapManager.js';
import CrumbManager from './CrumbManager.js';

import SnakeNode from './snake-node.js'
const SNAKE_MOVE_INTERVAL = 30;
const SNAKE_CLIP_PATH_INTERVAL = 500;
const VIEW_RANGE = 10000 // AI视野范围
const WARN_RANGE = 6400 // 警告范围
const EAT_RANGE = 3200 // 食物范围

class Snake {
	
	constructor({pos, dir, score, color, AI, stage, name, manager, pInitLength}) {
		this.snakeInfo = {
			snakeId: UniqueID.ID,
			snakeName: name,
			score: score, // 游戏分数
			initLength:  pInitLength || PLAYER_INIT_LENGTH, // 初始长度
			bodyLength: pInitLength|| PLAYER_INIT_LENGTH, // 身体长度
			color: color,
			headPos: pos, // vector
			isAi: AI,
			dieStatus: false,
			snakeHeadRadius: 30,
			bodyRadius: 36, // 身体半径
			invincibleTimer: 0, // 无敌计时器
			killQuantity: 0 // 击杀数量
		}
		this.scoreCoefficient = 1 // 得分系数
		this.bodyNodeList = [] // 身体数据
		this.bodyPathList = []
		this.snakeDir = dir			// 运动方向vector
		this.newAimDir = dir.clone()		// 新的方向
		this._tobe = 1
		this.snakeSpeed = INIT_SPEED
		this.gameMapLayer = stage // 根容器 gameMapLayer
		this._moveDelta = 0
		this._clipPathDelta = 0
		this._normalAIDelta = 0
		this._normalAIDirDelta = 0
		this.curSpeedScale = 1 // 速度
		
		this._warnRangeSqrt = 200
		this._textIdx = 0
		this.snakeManager = manager
		this.snakeHead = {}
		this.nextScaleScore = 300 // 下一阶段放大分数
		this.scaleNum = 1
		this.createBody()
		
	}
	get snakeId() {
		return this.snakeInfo.snakeId
	}
	get headPos() {
		return this.snakeInfo.headPos;
	}
	get snakeName() {
		return this.snakeInfo.snakeName;
	}
	get score() {
		return this.snakeInfo.score;
	}
	set score(value) {
		this.snakeInfo.score = value
		this.detectionNeedScale(this.snakeInfo.score)
	}
	get IsDead() {
		return this.snakeInfo.dieStatus;
	}

	get invincibleTimer() {
		return this.snakeInfo.invincibleTimer;
	}
	set invincibleTimer(value) {
		this.snakeInfo.invincibleTimer = value;
		if(!this.IsDead && value > 0) {
		} else {
			this.snakeHead.removeInvincibleAnimation()
		}
	}

	detectionNeedScale(score) {
		if(score <= this.nextScaleScore) {
			// console.log
		} else {
			this.nextScaleScore += 500
			this.scaleNum += 0.1
			if(this.scaleNum > 1.8) { this.scaleNum = 1.8}
			this.bodyNodeList.forEach((item, index) => {
				item.scaleAddNode(this.scaleNum)
			});
		}
		
	}
	createBody() {
		if(this.snakeInfo.bodyLength > 8) {
			this.scaleNum = 1.3
		} else  if(this.snakeInfo.bodyLength> 12) {
			this.scaleNum = 1.4
		} else if(this.snakeInfo.bodyLength> 16) {
			this.scaleNum = 1.5
		}
		let pathLength = this.snakeInfo.bodyLength << INTERVAL_PATH_POINT;
		let curBody = 0;
		for (let itr = 0; itr < pathLength; itr++) {
			let pt_new = new Point();
			this.initNextMemo(0, pt_new) // 第一个参数 itr 为0 设置渐出
			this.bodyPathList.push(pt_new);
			if (curBody << INTERVAL_PATH_POINT === itr) {
				this.generateNode(pt_new.x, pt_new.y, curBody === 0);
				++curBody;
			}
		}
		this.snakeInfo.snakeHeadRadius = this.bodyNodeList[0].getBounds().width / 2;
	}

	initNextMemo(itr, pt) {
		let scaler = this.snakeSpeed * itr * this.scaleNum
		let transMtx = new Matrix2D(1, 0, 0, 1, -this.snakeDir.x * scaler, -this.snakeDir.y * scaler)
		transMtx.transformPoint(this.snakeInfo.headPos.x, this.snakeInfo.headPos.y, pt)
	}
	
	nextMemo(itr, pt) {
		let scaler = this.snakeSpeed * itr * this.scaleNum
		let transMtx = new Matrix2D(1, 0, 0, 1, this.snakeDir.x * scaler, this.snakeDir.y * scaler);
		transMtx.transformPoint(this.snakeInfo.headPos.x, this.snakeInfo.headPos.y, pt);
	}

	generateNode(x, y, isHead) {
		const text = this.snakeInfo.snakeName
		const angle = Math.atan2(this.snakeDir.y, this.snakeDir.x)  * (180 / Math.PI)
		let body = new SnakeNode(this, text, isHead, angle);
		if(isHead) {
			this.snakeHead = body;
		}
		body.x = x;
		body.y = y;
		body.setParentId(this.snakeId);
		this.gameMapLayer.addChild(body);
		body.scaleAddNode(this.scaleNum)
		this.bodyNodeList.push(body);
		MapManager.AddToABlock(body);
	}
	/**
	 * 转向
	 * @param {time} updateDelta 转向时间
	 */
	rotateSnakeHead(updateDelta) {
		// debugger
		// 如果蛇的运动方向和新的方向不一致，  更改运动方向
		if (this.snakeDir.x != this.newAimDir.x || this.snakeDir.y != this.newAimDir.y) {
			let dot = this.snakeDir.x * this.newAimDir.x + this.snakeDir.y * this.newAimDir.y;
			let angle = Math.acos(dot) / Math.PI * 180; // Math.acos(dot) 反余弦函数， 求得 angle 为角度
			let theta = ROTATE_SPEED * updateDelta; // 移动角度差值
			if (Math.abs(angle) <= theta) { // Math.abs(angle) 绝对值，
				this.snakeDir = this.newAimDir.clone();
			} else {
				let angle = this._tobe * theta
				let transMtx = new Matrix2D()
				transMtx.rotate(angle).transformPoint(this.snakeDir.x, this.snakeDir.y, this.snakeDir)
			}
		}
	}

	snakeMove(updateDelta) {
		this._moveDelta += updateDelta;
		if (this._moveDelta > SNAKE_MOVE_INTERVAL) {
			this._moveDelta -= SNAKE_MOVE_INTERVAL;
			for (let i = 1; i <= this.curSpeedScale; i++) {
				let pt_new = new Point();
				this.nextMemo(i, pt_new)
				this.bodyPathList.unshift(pt_new);
			}
			this.snakeInfo.headPos = this.bodyPathList[0];
			this.bodyNodeList[0] && this.bodyNodeList[0].setTransform(this.snakeInfo.headPos.x, this.snakeInfo.headPos.y);
			//转动蛇头方向
			this.MoveBodies();
		}
		this._clipPathDelta += updateDelta;
		if (this._clipPathDelta >= SNAKE_CLIP_PATH_INTERVAL) {
			this._clipPathDelta -= SNAKE_CLIP_PATH_INTERVAL;
			let length = this.snakeInfo.bodyLength << INTERVAL_PATH_POINT
			this.bodyPathList.splice(length + 1);
		}
	}
	MoveBodies() {
		let idx, pos, node;
		for (let i = this.bodyNodeList.length - 1; i >= 0; i--) {
			idx = Math.min(i << INTERVAL_PATH_POINT, this.bodyPathList.length - 1);
			pos = this.bodyPathList[idx];
			node = this.bodyNodeList[i];
			MapManager.RefreshBlock(node, pos);
			node.setTransform(pos.x, pos.y);
		}
	}

	changeSpeedScale(value) {
		if (this.curSpeedScale !== value) {
			this.curSpeedScale = value;
		}
	}
	/**
	 * 计算食物 和蛇头之间的 直线距离
	 * @param {*} a 食物位置
	 * @param {*} b  蛇头位置
	 * @returns 
	 */
	getDistance(a, b) {
		return Math.pow(a.x - b.x, 2) + Math.pow(a.y - b.y, 2);
	}
	/**
	 * 吃食物 道具
	 */
	checkCrumb() {
		let region = MapManager.getRegion(this.snakeInfo.headPos);
		for (let [id, entity] of region) {
			if (entity.type === "CRUMB" || entity.type === "DIE_SNAKE") {
				if (this.getDistance(entity, this.snakeInfo.headPos) < (EAT_RANGE * Math.sqrt(this.scaleNum))) {
					this.score += parseInt(entity.score * this.scoreCoefficient);
					CrumbManager.deleteCrumb(entity, this.snakeInfo.headPos);
					region.delete(id);
				}
			} else if (entity.type === "PROP" && !this.snakeInfo.isAi) {
				if(entity) {
					this.invincibleTimer += 10 * 1000
					if(this.invincibleTimer > 10 * 1000) {
						this.invincibleTimer = 10 * 1000
					}
					console.log(this.getDistance(entity, this.snakeInfo.headPos))
					if (this.getDistance(entity, this.snakeInfo.headPos) < (EAT_RANGE * Math.sqrt(this.scaleNum))) {
						this.score += parseInt(entity.score * this.scoreCoefficient);
						CrumbManager.deleteCrumb(entity, this.snakeInfo.headPos);
						region.delete(id);
					}
				}
			}
		}
	}
	/**
	 * 死亡后 变成食物
	 */
	dieResolve() {
		let snakeLength = this.bodyNodeList.length
		const dieCoefficient = parseInt(snakeLength / 5)
		this.bodyNodeList.forEach((item, index) => {
			if((snakeLength < 30 && index % 2 === 0) || (snakeLength > 30 && index % 6 === 0)) {
				CrumbManager.addCrumb({
					x: item.x + Math.random() * 10 - 5,
					y: item.y + Math.random() * 10 - 5,
					r: this.snakeInfo.bodyRadius * this.scaleNum,
					color: this.snakeInfo.color,
					score: 10 + dieCoefficient,
					type: "DIE_SNAKE"
				})
			}
			MapManager.RemoveFromABlock(item)
			this.gameMapLayer.removeChild(item)
		});
		this.bodyNodeList = []
		this.bodyPathList = []
	}
	checkDie() {
		if (this.IsDead) return true; // 已经死亡
		let region = MapManager.getRegion(this.snakeInfo.headPos);
		// 区域内存在的节点
		for (let [id, entity] of region) {
			if (entity.type === "SNAKE_NODE" && entity.ParentID != this.snakeId) { // 存在其他蛇
				let len = this.getDistance(entity, this.snakeInfo.headPos);
				if (len < Math.pow(this.snakeInfo.snakeHeadRadius << 1, 2) && (this.invincibleTimer <= 0)) {
					this.snakeManager.getSnake(entity.ParentID) && this.snakeManager.getSnake(entity.ParentID).kill();
					if(this.snakeInfo.isAi) {
						this.dieResolve()
					}
					this.snakeInfo.dieStatus = true
					return true
				}
			}
		}
		const exceedBoundaryStatus = this.isExceedBoundary()
		if (!this.snakeInfo.dieStatus && exceedBoundaryStatus && this.snakeInfo.isAi) {
			this.dieResolve()
		}
		this.snakeInfo.dieStatus = exceedBoundaryStatus
		return
	}
	/**
	 * 是否超出边界
	 * @returns boolean 
	 */
	isExceedBoundary() {
		return (this.snakeInfo.headPos.x + this.snakeInfo.snakeHeadRadius > PLAYGROUND_WIDTH)
		|| (this.snakeInfo.headPos.x - this.snakeInfo.snakeHeadRadius < 0)
		|| (this.snakeInfo.headPos.y + this.snakeInfo.snakeHeadRadius > PLAYGROUND_HEIGHT)
		|| (this.snakeInfo.headPos.y - this.snakeInfo.snakeHeadRadius < 0)
	}
	updateLength(needAddLength) {
		let len = this.snakeInfo.initLength + Math.floor(this.score / GROW_SCORE_POINT);
		if(needAddLength && needAddLength > 0) {
			len += needAddLength
		}
		if (len > this.snakeInfo.bodyLength && this.snakeInfo.bodyLength < 100) {
			let pt_new = new Point();
			for (let q = len - this.snakeInfo.bodyLength, i = 1; i <= q; i++) {
				this.initNextMemo(i, pt_new)
				this.generateNode(pt_new.x, pt_new.y);
			}
			this.snakeInfo.bodyLength = len;
		}
	}
	/**
	 * 更新Ai 下一帧运动
	 * @param {time stamp} updateDelta  时间戳 更新间隔
	 * @returns 
	 */
	UpdateNormalAI(updateDelta) {
		this._normalAIDelta += updateDelta
		this._normalAIDirDelta -= updateDelta;
		if (this._normalAIDelta < SNAKE_MOVE_INTERVAL)
			return;

		this._normalAIDelta -= SNAKE_MOVE_INTERVAL;
		// avoid enemy
		let region = MapManager.getRegion(this.snakeInfo.headPos);
		let force = null, minLength = 0;
		for (let [id, entity] of region) {
			if (entity.type === "SNAKE_NODE" && entity.ParentID != this.snakeId) {
				let len = this.getDistance(entity, this.snakeInfo.headPos);
				if (len < WARN_RANGE && (minLength == 0 || len < minLength)) {
					force = new Point();
					force.x = this.snakeInfo.headPos.x - entity.x;
					force.y = this.snakeInfo.headPos.y - entity.y;
					minLength = len;
				}
			}
		}

		if (force !== null) {
			this.RotateTo(force);
			return;
		}

		// avoid border
		if (this.snakeInfo.headPos.x < this._warnRangeSqrt || PLAYGROUND_WIDTH - this.snakeInfo.headPos.x < this._warnRangeSqrt) {
			force = new Point();
			force.x = this.snakeInfo.headPos.x < this._warnRangeSqrt ? 1 : -1;
		}
		if (this.snakeInfo.headPos.y < this._warnRangeSqrt || PLAYGROUND_HEIGHT - this.snakeInfo.headPos.y < this._warnRangeSqrt) {
			force = new Point();
			force.y = this.snakeInfo.headPos.y < this._warnRangeSqrt ? 1 : -1;
		}

		if (force !== null) {
			this.RotateTo(force);
			return;
		}

		// find die body
		this.curSpeedScale = 1;
		for (let [id, entity] of region) {
			
			if (entity.type === "DIE_SNAKE") {
				let len = this.getDistance(entity, this.snakeInfo.headPos);
				if (len < VIEW_RANGE && (minLength == 0 || len < minLength)) {
					force = new Point();
					force.x = entity.x - this.snakeInfo.headPos.x;
					force.y = entity.y - this.snakeInfo.headPos.y;
					minLength = len;
				}
			}
		}

		if (force !== null) {
			this.RotateTo(force);
			this.curSpeedScale = 2;
			return;
		}
		// random move
		if (this._normalAIDirDelta < 0) {
			this._normalAIDirDelta = Math.random() * 5000;
			let x = Math.random() * 2 - 1 + this.snakeDir.x;
			let y = Math.random() * 2 - 1 + this.snakeDir.y;
			this.RotateTo(new Point(x, y));
			return;
		}
	}

	normalize(vec) {
		let mod = Math.sqrt(Math.pow(vec.x, 2) + Math.pow(vec.y, 2));
		vec.x /= mod;
		vec.y /= mod;
		return vec;
	}
	// 更改蛇运动方向
	RotateTo(vec) {
		this.newAimDir = this.normalize(vec)
		this._tobe = this.newAimDir.y * this.snakeDir.x - this.newAimDir.x * this.snakeDir.y >= 0 ? 1 : -1
	}
	/**
	 * 更新下一帧蛇的运动
	 * @param {time stamp} updateDelta  时间戳 更新间隔
	 * @returns 
	 */
	updateSnake(updateDelta) {
		if (this.checkDie()) return;
		if (this.snakeInfo.isAi) this.UpdateNormalAI(updateDelta); // 更新ai
		this.setInvincibleStatus(updateDelta)
		this.rotateSnakeHead(updateDelta); // 转向
		this.snakeMove(updateDelta); //移动
		if(this.curSpeedScale > 1) {
			// 气流流动
			for(let item of this.bodyNodeList) {
				// console.log(item)
				item.updateAccelerateAnimation()
			}
			// this.bodyNodeList[1] && this.bodyNodeList[1].updateAccelerateAnimation()
		} else {
			for(let item of this.bodyNodeList) {
				// console.log(item)
				item.removeAccelerateAnimation()
			}
			
		}
		this.snakeHead.updateSnakeHeadRotate(this.snakeDir)
		this.checkCrumb();
		this.updateLength();
	}
	/**
	 * 设置无敌状态
	 * @param {string} updateDelta 
	 * @returns 
	 */
	setInvincibleStatus(updateDelta, type) {
		if(type === 'init') {
			this.invincibleTimer = 10 * 1000
		} else {
			this.invincibleTimer -= updateDelta
			if(this.invincibleTimer <= 0 || !this.snakeInfo.invincibleTimer) {
				this.invincibleTimer = 0
			}
		}
		if(this.invincibleTimer > 0) {
			this.snakeHead.updateInvincibleAnimation()
		} else {
			this.snakeHead.removeInvincibleAnimation()
		}
	}
	/**
	 * 
	 * @param {boolean} value true 加速 false 减速
	 */
	setAccelerate (value) {
		this.changeSpeedScale(value ? 2 : 1);
	}
	/**
	 * 复活玩家
	 */
	revivePlayer() {
		this.snakeInfo.dieStatus = false
		let startPosition = new Point(PLAYGROUND_WIDTH / 2, PLAYGROUND_HEIGHT / 2) // 开始位置
		this.snakeInfo.headPos = startPosition
		this.bodyNodeList.forEach(item => {
			item.x = startPosition.x
			item.y = startPosition.y
		})
	}
	kill() {
		this.snakeInfo.killQuantity++
	}
	changeScoreCoefficient(coefficient) {
		this.scoreCoefficient = 2
		setTimeout(() => {
			this.scoreCoefficient = 1
		}, 10 * 1000)
	}
	
}

export default Snake;
